/**
 * 
 */
package rs.fon.rapidminer.operator.model.builder;

import com.rapidminer.example.AttributeWeights;
import com.rapidminer.example.Example;
import com.rapidminer.example.ExampleSet;
import com.rapidminer.operator.*;
import com.rapidminer.operator.learner.tree.*;
import com.rapidminer.tools.Tools;
import com.rapidminer.example.set.AbstractExampleSet;
import rs.fon.rapidminer.operator.learner.tree.SplittedExampleSet;
import rs.fon.rapidminer.operator.learner.tree.NominalSplitCondition;
import rs.fon.rapidminer.process.*;
import rs.fon.rapidminer.process.Process;
import com.rapidminer.example.Attribute;
import java.lang.reflect.Method;
import java.util.jar.*;
import rs.fon.rapidminer.process.Step;
import rs.fon.rapidminer.process.StepData;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Date;



/**
 * @author Nikola Nikolic
 * 
 */
public class TreeBuilder {
	
	
	
	Process process;
	private LeafCreator leafCreator = new DecisionTreeLeafCreator();
	boolean splitOK = true;
	Date startDate;
	
	public TreeBuilder(rs.fon.rapidminer.process.Process process)
	{
		this.process = process;
	}

	public Tree learnTree(ExampleSet exampleSet) throws OperatorException {
		// grow tree
		Tree root = new Tree((ExampleSet)exampleSet.clone());
		startDate = new Date();
		buildTree(root, exampleSet, 1);	
		
//		if (shouldStop(exampleSet, 0)) {
//			leafCreator.changeTreeToLeaf(root, exampleSet);
//		} else {
//			buildTree(root, exampleSet, 1); //Pravi inicijalni cvor
//		}

		// prune
		
		Step step = process.getProcessSteps().get(5);
		StepData stepData = step.getStepData();
		
		try					
		{
			Class<?> t =  Class.forName(stepData.getNameOfImplementationClass());
			Object c = t.newInstance();
			
			Method[] methods = t.getMethods();
			Method mainMethod = null;
			
			for (int j=0; j<methods.length; j++)
			{
				if (methods[j].getName().equals("prune"))
				{
					mainMethod = methods[j];
					break;
				}
			}					
			
			
			
			Object[] objekti = new Object[3];
			objekti[0] = root;
			objekti[1] = Double.parseDouble(stepData.getListOfParameters().get(0).getXenteredValue());
			objekti[2] = leafCreator;
			
			mainMethod.invoke(c, objekti);			
		}
		catch (Exception ex)
		{	
			System.out.println(ex.getMessage());System.out.println(ex.getMessage());
		}		

		return root;
	}
	
	@SuppressWarnings("unchecked")
	public void buildTree(Tree current, ExampleSet exampleSet, int depth)
	{
		ExampleSet trainingSet = (ExampleSet)exampleSet.clone();
		List<Step> steps = process.getProcessSteps();
		List<SplittedExampleSet> sviSplitedi = new LinkedList<SplittedExampleSet>();
		SplittedExampleSet splitted = null;
		Attribute bestAttribute = null;
		//double bestSplitValue = Double.NaN;
        boolean [] attributesForSplit = new boolean[exampleSet.getAttributes().size()];
	    splitOK=true; // Ovo treba srediti
		Step step; 
		StepData stepData = new StepData();
		List<StepData> multipleStepDataList = new LinkedList<StepData>();
		
		for(int t=0;t<attributesForSplit.length;t++)
		{
			attributesForSplit[t] = true;
		}
		
		for(int i=0; i<4; i++)
		{
			 step = steps.get(i);
			
			if (step.isMultiple())
			{
				multipleStepDataList = step.getMultipleStepData();
			}
			else				
			stepData = step.getStepData();
			
			switch(i)
			{
			
			case 0:
			{		
				try					
				{
					for(int k=0; k<multipleStepDataList.size();k++)
					{
					stepData = (StepData)multipleStepDataList.get(k);
					Class<?> t =  Class.forName(stepData.getNameOfImplementationClass());
					Object c = t.newInstance();
					
					Method[] methods = t.getMethods();
					Method mainMethod = null;
					
					for (int j=0; j<methods.length; j++)
					{
						if (methods[j].getName().equals("DoWork"))
						{
							mainMethod = methods[j];
							break;
						}
					}					
					
					Object[] objekti = new Object[4];
					objekti[0] = exampleSet;
					objekti[1] = stepData.getListOfParameters();
					objekti[2] = depth;
					objekti[3]=attributesForSplit;
					
					Object ChiSquaredWeightsObject = (Object)mainMethod.invoke(c, objekti);
					attributesForSplit =(boolean[])ChiSquaredWeightsObject;
					System.out.print("Dobro je! ");
					
					}
					
				}
				catch (Exception ex)
				{	
					System.out.print("Kurast jer je Exception! ");
				}				
				break;
			}
			
			
				case 1:
				{		
					try					
					{
						for(int k=0; k<multipleStepDataList.size();k++)
						{
						stepData = (StepData)multipleStepDataList.get(k);
						Class<?> t =  Class.forName(stepData.getNameOfImplementationClass());
						Object c = t.newInstance();
						
						Method[] methods = t.getMethods();
						Method mainMethod = null;
						
						for (int j=0; j<methods.length; j++)
						{
							if (methods[j].getName().equals("DoWork"))
							{
								mainMethod = methods[j];
								break;
							}
						}					
						
						Object[] objekti = new Object[5];
						objekti[0] = exampleSet;
						objekti[1] = stepData.getListOfParameters();
						objekti[2]= attributesForSplit;
						objekti[3] = 0.5;             
						objekti[4] = depth;
						Object sviSplitediObject = (Object)mainMethod.invoke(c, objekti);
						sviSplitedi.addAll((List<SplittedExampleSet>) sviSplitediObject);
						//sviSplitedi = (List<SplittedExampleSet>) sviSplitediObject;
						}
						
					}
					catch (Exception ex)
					{		
						System.out.println("Greska");
					}				
					break;
				}
				case 2:
				{
					try					
					{
						
						Class<?> t =  Class.forName(stepData.getNameOfImplementationClass());
						Object c = t.newInstance();
						
						Method[] methods = t.getMethods();
						Method mainMethod = null;
						
						Method getBenefitMethod = null;
						Method getBestAttributeMethod = null;
						
						for (int j=0; j<methods.length; j++)
						{
							if (methods[j].getName().equals("DoWork"))
							{
								mainMethod = methods[j];						
							}
							if (methods[j].getName().equals("GetBenefit"))
							{
								getBenefitMethod = methods[j];						
							}
							if (methods[j].getName().equals("GetBestAttribute"))
							{
								getBestAttributeMethod = methods[j];						
							}
						}					
						
						Object[] objekti = new Object[2];
						objekti[0] = sviSplitedi;
						objekti[1] = stepData.getListOfParameters();
						
						Object splittedObject = (Object)mainMethod.invoke(c, objekti);
						splitted = (SplittedExampleSet)splittedObject;
						
						Object benefitObject = (Object)getBenefitMethod.invoke(c,new Object[0]);
						double bestBenefit = Double.parseDouble(benefitObject.toString());
						
						if (bestBenefit == -1) {
							leafCreator.changeTreeToLeaf(current, (ExampleSet)exampleSet.clone());
							return;
						}
						
						objekti = new Object[1];
						objekti[0] = splitted;
						
						Object bestAttributeObject = (Object)getBestAttributeMethod.invoke(c,splitted);
						bestAttribute = (Attribute)bestAttributeObject;
						}
					
					catch (Exception ex)
					{		
						System.out.println("sd");
					}				
					break;
				}
				case 3:
				{
					
					if(splitted == null)
						break; 
					
					try					
					{

						for(int l=0; l<multipleStepDataList.size();l++)
						{
						stepData = (StepData)multipleStepDataList.get(l);
						Class<?> t =  Class.forName(stepData.getNameOfImplementationClass());
						Object c = t.newInstance();
						
						Method[] methods = t.getMethods();
						Method mainMethod = null;
						
						for (int j=0; j<methods.length; j++)
						{
							if (methods[j].getName().equals("DoWork"))
							{
								mainMethod = methods[j];
								break;
							}
						}	
					
						//stepData.getListOfParameters().get(0).setXenteredValue("7"); //HARDKODIRANO NAMESTANJE UNETE VREDNOSTI PRVOG PARAMETRA
						
						Object[] objekti = new Object[4];
						objekti[0] = exampleSet;
						objekti[1] = depth;
						objekti[2] = startDate;
						objekti[3] = stepData.getListOfParameters();
						
					
					      
						
						
						for (int k=0; k < splitted.getNumberOfSubsets(); k++)
						{
							splitted.selectSingleSubset(k);
							Object booleanObject = (Object)mainMethod.invoke(c, objekti);
							if(splitted.size() > 0 && (!Boolean.parseBoolean(booleanObject.toString())))
								splitOK = false;
							break;
							
							
							
						}	
						
						
						
						
						}
						
					}
					catch (Exception ex)
					{					
					}				
					break;					
				}	
				
			}			
		}	
		

		// if all have minimum size --> remove nominal attribute and recursive call for each subset
		List<String> kategorije = getAllCategories(exampleSet, exampleSet.getAttributes().getLabel());
		
		
		if (kategorije.size()==1 && depth!=1  )
		
		{
			
			splitOK=false;
			
		}
		
		if (splitOK) {
			//if (bestAttribute.isNominal()) {
		   // splitted.getAttributes().remove(bestAttribute);
			//}
			
			for (int i = 0; i < splitted.getNumberOfSubsets(); i++) {
				
				splitted.selectSingleSubset(i);
				
				
				if (splitted.size() > 0) {
					Tree child = new Tree((ExampleSet)splitted.clone());
					
					AbstractSplitCondition condition = null;
					if (bestAttribute.isNominal()) {
						//condition = new NominalSplitCondition(bestAttribute, bestAttribute.getMapping().mapIndex(i));
						LinkedList<String> ll =  splitted.getAllCategories(bestAttribute);
					
						condition = new NominalSplitCondition(bestAttribute, ll);
					} else {
						if (i == 0) {
							condition = new LessEqualsSplitCondition(bestAttribute, splitted.getSplitValue());
						} else {
							condition = new GreaterSplitCondition(bestAttribute, splitted.getSplitValue());
						}
					
					}
					
					
					current.addChild(child, condition);
					
					
					
					
					buildTree(child, splitted, depth + 1);
					
				}
			}
		} else {
			// min size not fulfilled: transform to leaf
			
			leafCreator.changeTreeToLeaf(current, exampleSet);
		
		
		}
	}
	
	
	
	 public  LinkedList<String> getAllCategories(ExampleSet exampleSet, Attribute attribute) {
	        LinkedList<String> allCategoryList  =  new LinkedList<String>();
	   
	       
	        Iterator<Example> reader = exampleSet.iterator();
	       
	        while (reader.hasNext()) {
	            Example example = reader.next();
	            String currentValue = example.getValueAsString(attribute);
	            if (!inList(currentValue,allCategoryList))
	                allCategoryList.add(currentValue);
	        }
	       
	        //return new SplittedExampleSet(exampleSet, partition);
	        return allCategoryList;
	    }
	 
	 public  boolean inList(String category,List<String> allCategoryList) {
	       
	        for(int i =0; i<allCategoryList.size();i++)
	        {
	            if (allCategoryList.get(i).equals(category))
	                return true;
	         
	        }
	       
	       return false;
	    }
	 
//	private boolean shouldStop(ExampleSet exampleSet, int depth) {
//		for(Terminator terminator : ) {
//			if (terminator.shouldStop(exampleSet, depth))
//				return true;
//		}
//		return false;
//	}
}
